/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package TimeRecording.business;

import TimeRecording.tables.AllocationOfHours;
import TimeRecording.tables.Employee;
import TimeRecording.tables.HolidayAdjustment;
import TimeRecording.tables.HolidayRequest;
import TimeRecording.tables.Session;
import TimeRecording.tables.SicknessReport;
import TimeRecording.tables.WeeklyTime;
import TimeRecording.tables.WorkingPattern;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import javax.persistence.EntityManager;

/**
 * This class holds raw data for holidays, sickness, timesheets, etc. for a
 * specified period of time.  The data is organised very much as it is in the
 * database.
 *
 * @author dpatchett
 */
public class RawPeriodData {

    protected Employee employee;

    protected DateRange dateRange;

    // Represents all of the working weeks in the period
    protected Collection<WeeklyTime> weeklyTimes;

    // Represents all holidays approved that "touch" the working period
    protected ArrayList<HolidayRequest> holidayRequests =
            new ArrayList<HolidayRequest>();

    // Represents all unapproved holidays that "touch" the working period
    protected ArrayList<HolidayRequest> unapprovedHolidayRequests =
            new ArrayList<HolidayRequest>();

    // Represents all approved holiday adjustments that "touch" the working period
    protected ArrayList<HolidayAdjustment> holidayAdjustments =
            new ArrayList<HolidayAdjustment>();

    // Represents all unapproved holiday adjustments that "touch" the working period
    protected ArrayList<HolidayAdjustment> unapprovedHolidayAdjustments =
            new ArrayList<HolidayAdjustment>();

    // Represents all sickness reports that "touch" the working period
    protected ArrayList<SicknessReport> sicknessReports =
            new ArrayList<SicknessReport>();

    // Represents all unapproved sickness reports that "touch" the working period
    protected ArrayList<SicknessReport> unapprovedSicknessReports =
            new ArrayList<SicknessReport>();

    // Working patterns that are "active" over at least some of the period
    protected Collection<WorkingPattern> workingPatterns;

    // Sessions that are active over the period
    protected Collection<Session> sessions;

    // Convenient calendars for parsings the working days for the period
    protected GregorianCalendar startCal;
    protected GregorianCalendar stopCal;

    public RawPeriodData() {
    }

    public RawPeriodData(
            EntityManager entityManager,
            Employee employee,
            DateRange dateRange) {

        refreshData(entityManager, employee, dateRange);
    }

    public boolean refreshRequired(Employee newEmployee,
            Date firstDay, Date lastDay) {

        return dateRange == null ||
                !dateRange.contains(firstDay) ||
                !dateRange.contains(lastDay) ||
                employee == null ||
                employee.getEmployeeId() != newEmployee.getEmployeeId();
    }

    protected void refreshData(EntityManager entityManager,
            Employee employee, DateRange dateRange) {

        this.employee = employee;
        this.dateRange = dateRange;
        refreshData(entityManager);
    }


    protected void refreshData(EntityManager entityManager) {

        startCal = new GregorianCalendar();
        startCal.setTime(dateRange.getFromDate());
        startCal = DaisyChainUtilities.firstDayOfWeek(startCal);

        stopCal = new GregorianCalendar();
        stopCal.setTime(dateRange.getToDate());
        stopCal = DaisyChainUtilities.lastDayOfWeek(stopCal);

        getWorkingPatterns(entityManager);
        getSessions(entityManager);
        getWeeklyTimes(entityManager);

        getSicknessReports(entityManager);
        getHolidayRequests(entityManager);
        getHolidayAdjustments(entityManager);
    }

    /**
     * Simply get details of any holiday requests that "touch" the period".
     */
    private void getHolidayRequests(EntityManager entityManager) {

        List<HolidayRequest> allHolidayRequests = HolidayRequest.
                getHolidaysForEmployeeAndDateRange(entityManager,
                        employee, dateRange).getResultList();

        holidayRequests.clear();
        unapprovedHolidayRequests.clear();

        for (HolidayRequest holidayRequest : allHolidayRequests) {
            if (holidayRequest.isApproved()) {
                holidayRequests.add(holidayRequest);
            } else {
                unapprovedHolidayRequests.add(holidayRequest);
            }
        }
    }

    /**
     * Simply get details of any holiday requests that "touch" the period".
     */
    private void getHolidayAdjustments(EntityManager entityManager) {

        List<HolidayAdjustment> allHolidayAdjustments = HolidayAdjustment.
                getHolidayAdjustmentsForEmployeeAndDateRange(entityManager,
                        employee, dateRange).getResultList();

        holidayAdjustments.clear();
        unapprovedHolidayAdjustments.clear();

        for (HolidayAdjustment holidayAdjustment : allHolidayAdjustments) {
            if (holidayAdjustment.isApproved()) {
                holidayAdjustments.add(holidayAdjustment);
            } else {
                unapprovedHolidayAdjustments.add(holidayAdjustment);
            }
        }
    }

    /**
     * Simply get details of any sickness reports that "touch" the period".
     */
    private void getSicknessReports(EntityManager entityManager) {

        List<SicknessReport> allSicknessReports = SicknessReport.
                getSicknessReportsForEmployeeAndDateRange(entityManager,
                        employee, dateRange).getResultList();

        sicknessReports.clear();
        unapprovedSicknessReports.clear();

        for (SicknessReport sicknessReport : allSicknessReports) {
            if (sicknessReport.isApproved()) {
                sicknessReports.add(sicknessReport);
            } else {
                unapprovedSicknessReports.add(sicknessReport);
            }
        }
    }

    /**
     * Simply get the weekly time records (time sheets) for the period.
     */
    private void getWeeklyTimes(EntityManager entityManager) {
        weeklyTimes = WeeklyTime.
                getWeeklyTimeByEmployeeAndDateRange(
                        entityManager, employee, dateRange);
    }

    /**
     * Simply get the weekly time records (time sheets) for the period.
     */
    private void getWorkingPatterns(EntityManager entityManager) {
        workingPatterns = WorkingPattern.
                getWorkingPatternsForEmployeeAndDateRange(
                        entityManager, employee, dateRange);
    }

    private void getSessions(EntityManager entityManager) {
        sessions = Session.getSessionsForEmployeeAndDateRange(
                        entityManager, employee, dateRange);
    }

    public WeeklyTime getWeeklyTime(GregorianCalendar cal) {

        for (WeeklyTime weeklyTime : weeklyTimes) {
            if (weeklyTime.inRange(cal)) {
                return weeklyTime;
            }
        }
        return null;
    }

    /**
     * This method will provide total figures for classes of data held within
     * the period data.  Note that this class holds details of complete weeks
     * but this method will only operate on dates that fall within the true date
     * range.
     *
     * @return totalled figures for the core period
     */
    public AllocationOfHours getTotals() {

        AllocationOfHours totalAllocation = new AllocationOfHours();
        AllocationOfHours dailyAllocation;

        GregorianCalendar startOfMonth = new GregorianCalendar();
        startOfMonth.setTime(dateRange.getFromDate());

        GregorianCalendar endOfMonth = new GregorianCalendar();
        endOfMonth.setTime(dateRange.getToDate());

        GregorianCalendar date = new GregorianCalendar();

        for (WeeklyTime timesheet : weeklyTimes) {

            date.setTime(timesheet.getWeeklyTimePK().getWeekCommencing());

            for (int day = 0; day < 7; day++) {

                int dayOfWeek = date.get(GregorianCalendar.DAY_OF_WEEK);

                dailyAllocation = timesheet.getDailyFigures(dayOfWeek);
                if (!date.before(startOfMonth) && !date.after(endOfMonth)) {
                    totalAllocation.add(dailyAllocation);
                }

                date.add(GregorianCalendar.DATE, 1);
            }
        }

        return totalAllocation;
    }

    /**
     * This method adds the supplied holiday request to the underlying
     * collection of holidays.  If the request is already there (or at least its
     * ID is) then it is removed and the new one added.
     *
     * @param localHolidayRequest
     */
    public void insertHolidayRequest(HolidayRequest localHolidayRequest) {

        ArrayList<HolidayRequest> holidays;
        if (localHolidayRequest.isApproved()) {
            holidays = holidayRequests;
        } else {
            holidays = unapprovedHolidayRequests;
        }

        // Plumb the holiday request into our collection.  If it's already in
        // there (based  on ID) then remove the one from the collection and
        // replace it with that which is passed in.

        if (localHolidayRequest.getRequestId() != null) {
            for (HolidayRequest holidayRequest : holidays) {
                if (holidayRequest.getRequestId().equals(
                        localHolidayRequest.getRequestId())) {
                    holidays.remove(holidayRequest);
                    break;
                }
            }
        }

        // Add the new (or changed) holiday into the collection
        holidays.add(localHolidayRequest);
    }

    /**
     * This method adds the supplied holiday request to the underlying
     * collection of holidays.  If the request is already there (or at least its
     * ID is) then it is removed and the new one added.
     *
     * @param localHolidayRequest
     */
    public void insertSicknessReport(SicknessReport localSicknessReport) {

        ArrayList<SicknessReport> sicknesses;
        if (localSicknessReport.isApproved()) {
            sicknesses = sicknessReports;
        } else {
            sicknesses = unapprovedSicknessReports;
        }

        // Plumb the sickness report into our collection.  If it's already in
        // there (based  on ID) then remove the one from the collection and
        // replace it with that which is passed in.

        if (localSicknessReport.getReportId() != null) {
            for (SicknessReport sicknessReport : sicknesses) {
                if (sicknessReport.getReportId().equals(
                        localSicknessReport.getReportId())) {
                    sicknesses.remove(sicknessReport);
                    break;
                }
            }
        }

        // Add the new (or changed) holiday into the collection
        sicknesses.add(localSicknessReport);
    }
}
